package invertedindex;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;

public class Encoding {
	private static int mask = 0x7F;
	private static int hasNext = 0x80;
	private static short maxSize = 255;

	// This function constructs from string back to document_id count pair.
	public static SortedMap<Integer, Integer> Strings2Map(SortedMap<Integer, String> queryResult) {
		SortedMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
		Iterator<Integer> i = queryResult.keySet().iterator();
		int key;
		
		while(i.hasNext()) {
			key = i.next();
			String[] encoding = queryResult.get(key).split(" ");

			// Go over each document_id(or delta)- counter pair
			for (int j = 0; j < encoding.length; j++) {
				String pair = encoding[j];
				int k = 0;
				int curKey = 0;
				int value = 0;
				String hex = "";
				boolean hasNextByte = true;
				while (k < pair.length() / 2 + 1) {
					if (hasNextByte) {
						hex = pair.substring(k * 2, (k + 1) * 2);
						int current = hexString2Number(hex);
						current = current << (7 * k);
						curKey += current;
						hasNextByte = hasNext(hex);
						// System.out.println("[string2Map] hex " + hex +
						// ", current: " + current + "\tkey:" + curKey);
						k++;
					} else {
						value = Integer.parseInt(pair.substring(k * 2, pair
								.length()));
						// System.out.println("[string2Map]" + value);
						k = pair.length();
					}

				}
				// If the curKey is not the first in the list, then add the delta value
				if (j > 0)
					key += curKey;
				// System.out.println("[string2Map] key " + key +", value: " +
				// value);
				map.put(key, value);
			}
		}
		return map;
	}
	
	 // Input:	SortedMap<Integer, Integer>	where key is the doc_id and value is the count
    // Output:	ArrayList<String>			String encoding of doc_id (or delta) in hex and count
    public static SortedMap<Integer, String> Map2Strings(SortedMap<Integer, Integer> map){

    		SortedMap<Integer, String> sequence = new TreeMap<Integer, String>();				// To store a set of string encodings
            String encode = new String();
            String buffer = new String();
            Iterator<Integer> i = map.keySet().iterator();

            // Get the first document id and count
            int lastKey = i.next();
            int firstKey = lastKey;
            encode =  contructStringPair(lastKey, map.get(lastKey));
            if (buffer.length() + encode.length() < maxSize)
                    buffer += encode + " ";
            else {
                    sequence.put(firstKey, buffer);
                    buffer = encode + " ";
            }

            // Get the sequential document id and count
            while(i.hasNext()){
                    int key = i.next();
                    // Calculate the delta value for current document id with the previous one
                    int delta = key - lastKey;
                    encode = contructStringPair(delta, map.get(key));
                    // Check if the buffer is full after adding the current string encoding if not
                    // add the encoding into buffer, otherwise, "flush" the buffer into the string arraylist
                    // and then write the encoding to the buffer
                    if (buffer.length() + encode.length() < maxSize)
                            buffer += encode + " ";
                    else {
                            sequence.put(firstKey, buffer.trim());
                            encode = contructStringPair(key, map.get(key));
                            buffer = encode + " ";
                            firstKey = key;
                    }
                    //System.out.println("[map2String]:" + key + ", " + delta + ", " + lastKey + "--"+ encode +"\n"+ buffer);
                    lastKey = key;		
            }
            // Flush the buffer to the string arraylist
            sequence.put(firstKey, buffer.trim());
            return sequence;	        
    }

	// convert from hex to decimal
	private static int hexString2Number(String s) {
		int tmp = Integer.parseInt(s, 16);
		int current = tmp & mask;
		return current;
	}

	// Check if there is subsequence bit
	private static boolean hasNext(String s) {
		int tmp = Integer.parseInt(s, 16);
		int current = tmp & hasNext;
		if (current == 0)
			return false;
		return true;
	}
	


    // This method builds a string of document_id (or delta) in hex rep. and counter 
    private static String contructStringPair(int key, int value){
            return number2String(key) + value;
    }

    // This method is used to convert the document id (or delta) from integer into "hex" encoding (string)
    private static String number2String(int num){
            String stringRep = "";
            int tmp = num;

            byte index = -1;
            ArrayList<Integer> list = new ArrayList<Integer>();

            // Convert an integer in a list of 7-bit entities 
            for (int i = 0; i < Integer.SIZE ; i++){
                    list.add((tmp & mask));
                    tmp = tmp >> 7;
            }

            // add 128 (set to 1 the first bit) if a byte is preceeding the current byte.
            // observe we start from the end of the list
            for (int i = Integer.SIZE - 1; 0 < i; i--){
                    if (list.get(i) != 0){
                            tmp = list.get(i-1) + hasNext;
                            list.remove(i-1);
                            list.add(i-1, tmp);
                    }
                    else
                            index = (byte)i;
            }

            for (int i = 0; i < index; i++){
                    String s = Integer.toHexString(list.get(i));
                    if (s.length() == 1){
                            s = "0" + s;
                    }
                    stringRep += s;
                    //System.out.println("[number2String] num is " + num + "\t" + current + " " + Integer.toHexString(list.get(i)) + " " + ((char)current));
            }

            //System.out.println("[number2String] index:" + index + "\tStringRep: " + stringRep);
            return stringRep;
    }
}
